﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using DingHeng.TransportationManagement.Entity;

namespace DingHeng.TransportationManagement.DataAccess
{
    public class DAOBase<T> : IDisposable where T : class
    {
        protected TransportationManagementContext context { get; set; }

        /// <summary>
        /// 创建BllBase
        /// </summary>
        public DAOBase()
        {

            //Context.Configuration.LazyLoadingEnabled = false;
            //Context.Configuration.ProxyCreationEnabled = false;
            //Context.Configuration.ValidateOnSaveEnabled = false;
        }
        /// <summary>
        /// 插入实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public virtual int Insert(T entity)
        {
            this.context.Set<T>().Add(entity);
            return context.SaveChanges();
        }

        /// <summary>
        /// 批量插入实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collections">实体集合</param>
        /// <returns></returns>
        public virtual int Insert(List<T> entities)
        {
            using (context = new TransportationManagementContext())
            {
                context.Set<T>().AddRange(entities);
                return context.SaveChanges();
            }
        }

        public virtual void Update(T oldEntity, T newEntity)
        {
            foreach (PropertyInfo property in oldEntity.GetType().GetProperties())
            {
                if (property.Name != "ID")
                {
                    property.SetValue(oldEntity, property.GetValue(newEntity));
                }
            }
        }

        public virtual int SaveChanges()
        {
            if (context != null)
            {
                return context.SaveChanges();
            }
            return 0;
        }

        /// <summary>
        /// 根据条件删除实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="condition">条件</param>
        /// <returns></returns>
        public virtual int DeleteByCondition(Expression<Func<T, bool>> condition)
        {
            using (context = new TransportationManagementContext())
            {
                DbQuery<T> query = context.Set<T>();
                var collections = query.Where(condition);
                context.Set<T>().RemoveRange(collections);
                return context.SaveChanges();
            }
        }


        /// <summary>
        /// 加载实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="condition">条件</param>
        /// <param name="tableName">子类型的表名</param>
        /// <returns></returns>
        public virtual T Load(Expression<Func<T, bool>> condition, params string[] tableName)
        {
            using (context = new TransportationManagementContext())
            {
                DbQuery<T> query = context.Set<T>();
                foreach (string t in tableName)
                {
                    query = query.Include(t);
                }
                return query.Where(condition).FirstOrDefault();
            }
        }

        /// <summary>
        /// 加载所有实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="tableName">子类型的表名</param>
        /// <returns></returns>
        public virtual IQueryable<T> LoadAll(params string[] tableName)
        {
            context = new TransportationManagementContext();
            {
                DbQuery<T> query = context.Set<T>();
                foreach (string t in tableName)
                {
                    query = query.Include(t);
                }
                return query.AsQueryable();
            }
        }

        /// <summary>
        /// 根据条件加载实体
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="condition">条件</param>
        /// <param name="tableName">子类型的表名</param>
        /// <returns></returns>
        public virtual IQueryable<T> LoadByCondition(Expression<Func<T, bool>> condition, params string[] tableName)
        {
            context = new TransportationManagementContext();
            DbQuery<T> query = context.Set<T>();
            foreach (string t in tableName)
            {
                query = query.Include(t);
            }
            return query.Where(condition);
        }

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="condition">条件</param>
        /// <returns></returns>
        public virtual bool Exist(Expression<Func<T, bool>> condition)
        {
            using (context = new TransportationManagementContext())
            {
                return context.Set<T>().Any(condition);
            }
        }

        /// <summary>
        /// 返回个数
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <returns></returns>
        public virtual int Count()
        {
            using (context = new TransportationManagementContext())
            {
                return context.Set<T>().Count();
            }

        }

        /// <summary>
        /// 返回符合条件的个数
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="condition">条件</param>
        /// <returns></returns>
        public virtual int Count(Expression<Func<T, bool>> condition)
        {
            using (context = new TransportationManagementContext())
            {
                return context.Set<T>().Count(condition);
            }
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="M">类型</typeparam>
        /// <param name="list">数据源列表</param>
        /// <param name="pagerCondition">分页条件</param>
        /// <returns>返回分页后列表</returns>
        public void GetPagerList(IQueryable<T> list, Pager<T> pagerCondition)
        {
            pagerCondition.CurrentPageList.Clear();

            if (pagerCondition.PageSize <= 0 || pagerCondition.PageIndex < 1)
            {
                pagerCondition.PageCount = 0;
                pagerCondition.PageIndex = 0;

                return;
            }

            IQueryable<T> iquery = list;

            int rowSkip = -1;

            pagerCondition.TotalCount = iquery.Count();

            if (pagerCondition.TotalCount == 0)
            {
                pagerCondition.PageCount = 0;
                pagerCondition.PageIndex = 0;

                return;
            }

            int mode = Convert.ToInt32(pagerCondition.TotalCount % Convert.ToInt64(pagerCondition.PageSize));

            if (mode == 0)
            {
                pagerCondition.PageCount = Convert.ToInt32(pagerCondition.TotalCount / Convert.ToInt64(pagerCondition.PageSize));
            }
            else
            {
                pagerCondition.PageCount = Convert.ToInt32(pagerCondition.TotalCount / Convert.ToInt64(pagerCondition.PageSize)) + 1;
            }

            //
            //如果当前页码超过页总数，则取页总数为当前页码
            //

            if (pagerCondition.PageIndex > pagerCondition.PageCount)
            {
                pagerCondition.PageIndex = pagerCondition.PageCount;
            }

            rowSkip = pagerCondition.PageSize * (pagerCondition.PageIndex - 1);

            if (string.IsNullOrEmpty(pagerCondition.OrderBy) == true)
            {
                pagerCondition.OrderBy = "CreateDate DESC";
            }

            IQueryable<T> result = OrderUsingSortExpression(iquery, pagerCondition.OrderBy).Skip(rowSkip).Take(pagerCondition.PageSize);

            foreach (var c in result)
            {
                pagerCondition.CurrentPageList.Add(c);
            }
        }

        private IQueryable<T> OrderBy(IQueryable<T> source, string fieldName)
        {
            MethodCallExpression resultExp = GenerateMethodCall(source, "OrderBy", fieldName);
            return source.Provider.CreateQuery<T>(resultExp) as IQueryable<T>;
        }

        private IQueryable<T> OrderByDescending(IQueryable<T> source, string fieldName)
        {
            MethodCallExpression resultExp = GenerateMethodCall(source, "OrderByDescending", fieldName);
            return source.Provider.CreateQuery<T>(resultExp) as IQueryable<T>;
        }

        private IQueryable<T> ThenBy(IQueryable<T> source, string fieldName)
        {
            MethodCallExpression resultExp = GenerateMethodCall(source, "ThenBy", fieldName);
            return source.Provider.CreateQuery<T>(resultExp) as IQueryable<T>;
        }

        private IQueryable<T> ThenByDescending(IQueryable<T> source, string fieldName)
        {
            MethodCallExpression resultExp = GenerateMethodCall(source, "ThenByDescending", fieldName);
            return source.Provider.CreateQuery<T>(resultExp) as IQueryable<T>;
        }

        public IQueryable<T> OrderUsingSortExpression(IQueryable<T> source, string sortExpression)
        {
            String[] orderFields = sortExpression.Split(',');
            IQueryable<T> result = null;
            for (int currentFieldIndex = 0; currentFieldIndex < orderFields.Length; currentFieldIndex++)
            {
                String[] expressionPart = orderFields[currentFieldIndex].Trim().Split(' ');
                String sortField = expressionPart[0];
                Boolean sortDescending = (expressionPart.Length == 2) && (expressionPart[1].Equals("DESC", StringComparison.OrdinalIgnoreCase));
                if (sortDescending)
                {
                    result = currentFieldIndex == 0 ? OrderByDescending(source, sortField) : ThenByDescending(result, sortField);
                }
                else
                {
                    result = currentFieldIndex == 0 ? OrderBy(source, sortField) : ThenBy(result, sortField);
                }
            }
            return result;
        }

        private MethodCallExpression GenerateMethodCall(IQueryable<T> source, string methodName, String fieldName)
        {
            Type type = typeof(T);
            Type selectorResultType;
            LambdaExpression selector = GenerateSelector(fieldName, out selectorResultType);
            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
                            new Type[] { type, selectorResultType },
                            source.Expression, Expression.Quote(selector));
            return resultExp;
        }

        private LambdaExpression GenerateSelector(String propertyName, out Type resultType)
        {
            // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField).
            var parameter = Expression.Parameter(typeof(T), "Entity");
            //  create the selector part, but support child properties
            PropertyInfo property;
            Expression propertyAccess;
            if (propertyName.Contains('.'))
            {
                // support to be sorted on child fields.
                String[] childProperties = propertyName.Split('.');
                property = typeof(T).GetProperty(childProperties[0]);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
                for (int i = 1; i < childProperties.Length; i++)
                {
                    property = property.PropertyType.GetProperty(childProperties[i]);
                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                }
            }
            else
            {
                property = typeof(T).GetProperty(propertyName);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
            }
            resultType = property.PropertyType;
            // Create the order by expression.
            return Expression.Lambda(propertyAccess, parameter);
        }


        #region IDisposable 成员


        public void Dispose()
        {
            if (context != null)
            {
                context.Dispose();
            }
        }

        #endregion
    }
}
